package com.rainy.generator.manager;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.rainy.generator.common.Constant;
import com.rainy.generator.common.config.JsonConfig;
import com.rainy.generator.entity.ColumnEntity;
import com.rainy.generator.entity.PathEntity;
import com.rainy.generator.entity.TableEntity;
import com.rainy.generator.entity.TemplatePath;
import com.rainy.generator.service.PathService;
import com.rainy.generator.service.TableService;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author liuqin on 2017/12/16
 */
@Component
public class GeneratorManager {

    @Resource
    private TableService tableService;

    @Resource
    private PathService pathService;

    @Resource
    private JsonConfig jsonConfig;

    public  List<String> getTemplates(){
        List<String> templates = Lists.newArrayList();
        templates.add("generator/entity.java.vm");
        templates.add("generator/dao.java.vm");
        templates.add("generator/mapper.xml.vm");
        templates.add("generator/service.java.vm");
        templates.add("generator/serviceImpl.java.vm");
        //templates.add("generator/BaseDao.java.vm");
        return templates;
    }


     Map<String,String> config = Maps.newHashMap();
    public  byte[] execute(String db,String[] tableNames,Long pathId) {
        config.put("package","com.rainy.sso.common");
        config.put("entity","com.rainy.sso.client");
        config.put("mainPath","com.rainy.sso.common");
        config.put("author","liuqin");
        config.put("int","Integer");
        config.put("integer","Integer");
        config.put("bigint","Long");
        config.put("float","Float");
        config.put("double","Double");
        config.put("decimal","BigDecimal");
        config.put("char","String");
        config.put("varchar","String");
        config.put("date","Date");
        config.put("datetime","Date");
        config.put("timestamp","Date");
        config.put("tinyint","int");
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);

        for(String tableName : tableNames){
            //查询表信息
            TableEntity table = tableService.queryTable(db,tableName);
            //查询列信息
            List<Map<String,String>> columns = tableService.queryColumns(db,tableName);
            //列信息
            List<ColumnEntity> columnList = Lists.newArrayList();
            buildColumn(table, columns,columnList);
            load(table,zip,columnList,pathId);
        }
        IOUtils.closeQuietly(zip);

        return outputStream.toByteArray();
    }

    private  void buildColumn(TableEntity table, List<Map<String,String>> columns,List<ColumnEntity> columnList){
        for(Map<String, String> column : columns) {
            ColumnEntity columnEntity = new ColumnEntity();
            columnEntity.setColumnName(column.get("columnName"));
            columnEntity.setDataType(column.get("dataType"));
            columnEntity.setComments(column.get("columnComment"));
            columnEntity.setExtra(column.get("extra"));

            //列名转换成Java属性名
            String attrName = columnToJava(columnEntity.getColumnName());
            columnEntity.setAttrName(attrName);
            columnEntity.setAttrNameLow(StringUtils.uncapitalize(attrName));

            //列的数据类型，转换成Java类型
            String attrType = config.get(columnEntity.getDataType());
            columnEntity.setAttrType(attrType);

            //是否主键
            if ("PRI".equalsIgnoreCase(column.get("columnKey" )) && table.getPk() == null) {
                table.setPk(columnEntity);
            }
            columnList.add(columnEntity);
        }
        //table.setColumns(columsList);
        //没主键，则第一个字段为主键
        if (table.getPk() == null) {
            table.setPk(columnList.get(0));
        }
    }

    private  void load(TableEntity table, ZipOutputStream zip, List<ColumnEntity> columnList, Long pathId) {

        //设置velocity资源加载器
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader" );
        Velocity.init(prop);

        //封装模板数据
        Map<String, Object> map = Maps.newHashMap();
        PathEntity pathEntity = pathService.queryPathById(pathId);
        if (pathEntity==null){
            pathEntity = new PathEntity();
        }
        String basePackage = pathEntity.getBasePackage();
        if (StringUtils.isBlank(basePackage)){
            basePackage = Constant.PATH_BASE_PACKAGE;
        }

        String className = tableToClass(table.getTableName().replace(Constant.TABLE_PRE,""));
        String classname = StringUtils.uncapitalize(className);
        map.put(Constant.PATH_ENTITY,this.getPath(basePackage,pathEntity.getEntity(),Constant.PATH_ENTITY));
        map.put(Constant.PATH_DAO,this.getPath(basePackage,pathEntity.getDao(),Constant.PATH_DAO));
        map.put(Constant.PATH_SERVICE,this.getPath(basePackage,pathEntity.getService(),Constant.PATH_SERVICE));
        map.put("serviceImpl",this.getPath(basePackage,pathEntity.getServiceImpl(),Constant.PATH_SERVICE+"."+Constant.PATH_IMPL));
        map.put("controller",this.getPath(basePackage,pathEntity.getServiceImpl(),Constant.PATH_CONTROLLER));
        map.put("className",className);
        map.put("classname",classname);
        map.put("tableName",table.getTableName());
        map.put("tableComment",table.getTableComment());
        map.put("columns",columnList);
        map.put("useLombok",true);
        map.put("hasDate",false);
        map.put("author","liuqin");
        map.put("datetime", getFormatDateStr());
        map.put("pk", table.getPk());
        VelocityContext context = new VelocityContext(map);

        TemplatePath templatePath = jsonConfig.getConfig(jsonConfig.getTemplatePath(),TemplatePath.class);
        String pathPre = templatePath.getPathPre();
        String ignoreSuf = templatePath.getIgnoreSuf();
        //获取模板列表
        List<String> templates = templatePath.getList();
        for (String template : templates) {
            //渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(pathPre+"/"+template, "UTF-8" );
            tpl.merge(context, sw);
            try {
                //添加到zip
                String fileName;
                if (template.contains("xml")){
                    fileName = getResourceFileName(template,classname);
                }else {
                    fileName = getJavaFileName(map, template, basePackage, className,ignoreSuf);
                }
                zip.putNextEntry(new ZipEntry(fileName));
                IOUtils.write(sw.toString(), zip, "UTF-8" );
                IOUtils.closeQuietly(sw);
                zip.closeEntry();
            } catch (IOException e) {
                //throw new RRException("渲染模板失败，表名：" + table.getTableName(), e);
            }
        }

    }

    private String getFormatDateStr() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return formatter.format(currentTime);
    }

    /**
     * 获取路径
     */
    private String getPath(String basePackage,String path,String module){
        return StringUtils.isNotBlank(path)?path:basePackage+"."+module;
    }

    /**
     * 列名转换成Java属性名
     */
    private  String columnToJava(String columnName) {
        return Arrays.stream(columnName.split("_")).map(v->StringUtils.capitalize(v)).reduce((s, s2) -> s.concat(s2)).get();
    }

    /**
     *
     * @param columnName
     * @return
     */
    private  String tableToClass(String columnName) {
        return Arrays.stream(columnName.split("_")).map(v->StringUtils.capitalize(v)).reduce((s, s2) -> s.concat(s2)).get();
    }

    /**
     * 获取文件名
     */
    private String getJavaFileName(Map<String, Object> map, String template, String basePackage, String className ,String ignoreSuf) {
        StringBuilder packagePath = new StringBuilder(Constant.BASE_JAVA_PATH);
        String[] names = template.split("\\.");
        if (names.length>=2) {
            String path = String.valueOf(map.get(names[0]));
            if (StringUtils.isBlank(path)) {
                path = basePackage;
            }
            packagePath.append(path.replace(".", File.separator))
                    .append(File.separator)
                    .append(className);
            if (!ignoreSuf.contains(names[0])) {
                packagePath.append(StringUtils.capitalize(names[0]));
            }
            packagePath.append(".")
                    .append(names[1]);
        }
        return packagePath.toString();
    }

    /**
     * 获取文件名
     */
    private String getResourceFileName(String template,String classname) {
            StringBuilder packagePath = new StringBuilder(Constant.BASE_RESOURCE_PATH);
            String[] names = template.split("\\.");
            packagePath.append(names[0])
                    .append(File.separator)
                    .append(classname)
                    .append(StringUtils.capitalize(names[0]))
                    .append(".")
                    .append("xml");
        return packagePath.toString();
    }

}

